package com.gitee.wsl.security.hash

import com.gitee.wsl.ext.array.readInt
import com.gitee.wsl.ext.array.readLong
import com.gitee.wsl.io.Charset
import com.gitee.wsl.io.encodeToByteArray
import com.gitee.wsl.lang.ByteOrder

/**
 * @author Sam Harwell
 */
object MurmurHash {
    private const val DEFAULT_SEED = 0

    /**
     * Initialize the hash using the specified [seed].
     *
     * @param seed The seed
     * @return The intermediate hash value
     */
     fun initialize(seed: Int = DEFAULT_SEED): Int = seed

    /**
     * Update the intermediate hash value for the next input [value].
     *
     * @param hash The intermediate hash value
     * @param value The value to add to the current hash
     * @return The updated intermediate hash value
     */
     fun update(hash: Int, value: Int): Int {
        var tempHash = hash
        val c1 = 0xCC9E2D51.toInt()
        val c2 = 0x1B873593
        val r1 = 15
        val r2 = 13
        val m = 5
        val n = 0xE6546B64.toInt()

        var k = value
        k *= c1
        k = (k shl r1) or (k ushr (32 - r1))
        k *= c2

        tempHash = tempHash xor k
        tempHash = (tempHash shl r2) or (tempHash ushr (32 - r2))
        tempHash = tempHash * m + n
        return tempHash
    }

    /**
     * Update the intermediate hash value for the next input [value].
     *
     * @param hash The intermediate hash value
     * @param value The value to add to the current hash
     * @return The updated intermediate hash value
     */
     fun update(hash: Int, value: Any?): Int =
        update(hash, value?.hashCode() ?: 0)

    /**
     * Apply the final computation steps to the intermediate value [hash]
     * to form the final result of the MurmurHash 3 hash function.
     *
     * @param hash The intermediate hash value
     * @param numberOfWords The number of integer values added to the hash
     * @return The final hash result
     */
     fun finish(hash: Int, numberOfWords: Int): Int {
        var tempHash = hash
        tempHash = tempHash xor numberOfWords * 4
        tempHash = tempHash xor (tempHash ushr 16)
        tempHash *= 0x85EBCA6B.toInt()
        tempHash = tempHash xor (tempHash ushr 13)
        tempHash *= 0xC2B2AE35.toInt()
        tempHash = tempHash xor (tempHash ushr 16)
        return tempHash
    }

    /**
     * Utility function to compute the hash code of an array using the MurmurHash algorithm.
     *
     * @param T The array element type
     * @param data The array data
     * @param seed The seed for the MurmurHash algorithm
     * @return The hash code of the data
     */
     fun <T> hashCode(data: Array<T>, seed: Int): Int {
        var hash = initialize(seed)

        for (value in data) {
            hash = update(hash, value)
        }

        hash = finish(hash, data.size)
        return hash
    }


    // Constants for 32 bit variant
    private const val C1_32: Int = -0x3361d2af
    private const val C2_32: Int = 0x1b873593
    private const val R1_32: Int = 15
    private const val R2_32: Int = 13
    private const val M_32: Int = 5
    private const val N_32: Int = -0x19ab949c

    // Constants for 128 bit variant
    private const val C1: Long = -0x783c846eeebdac2bL
    private const val C2: Long = 0x4cf5ad432745937fL
    private const val R1: Int = 31
    private const val R2: Int = 27
    private const val R3: Int = 33
    private const val M: Int = 5
    private const val N1: Int = 0x52dce729
    private const val N2: Int = 0x38495ab5

    private val DEFAULT_ORDER: ByteOrder = ByteOrder.LITTLE_ENDIAN

    /**
     * Murmur3 32-bit Hash值计算
     *
     * @param data 数据
     * @return Hash值
     */
    fun hash32(data: CharSequence): Int {
        return hash32(data.encodeToByteArray())
    }

    /**
     * Murmur3 32-bit Hash值计算
     *
     * @param data 数据
     * @return Hash值
     */
    fun hash32(data: ByteArray): Int {
        return hash32(data, data.size, DEFAULT_SEED)
    }

    /**
     * Murmur3 32-bit Hash值计算
     *
     * @param data   数据
     * @param length 长度
     * @param seed   种子，默认0
     * @return Hash值
     */
    fun hash32(data: ByteArray, length: Int, seed: Int): Int {
        return hash32(data, 0, length, seed)
    }

    /**
     * Murmur3 32-bit Hash值计算
     *
     * @param data   数据
     * @param offset 数据开始位置
     * @param length 长度
     * @param seed   种子，默认0
     * @return Hash值
     */
    fun hash32(data: ByteArray, offset: Int, length: Int, seed: Int): Int {
        var hash = seed
        val nblocks = length shr 2

        // body
        for (i in 0..<nblocks) {
            val i4 = offset + (i shl 2)
            val k: Int = data.readInt (i4)
            // mix functions
            hash = mix32(k, hash)
        }

        // tail
        val idx = offset + (nblocks shl 2)
        var k1 = 0
        when (offset + length - idx) {
            3 -> {
                k1 = k1 xor ((data[idx + 2].toInt() and 0xff) shl 16)
                k1 = k1 xor ((data[idx + 1].toInt() and 0xff) shl 8)
                k1 = k1 xor (data[idx].toInt() and 0xff)

                // mix functions
                k1 *= C1_32
                k1 = k1.rotateLeft(R1_32)
                k1 *= C2_32
                hash = hash xor k1
            }

            2 -> {
                k1 = k1 xor ((data[idx + 1].toInt() and 0xff) shl 8)
                k1 = k1 xor (data[idx].toInt() and 0xff)

                k1 *= C1_32
                k1 = k1.rotateLeft(R1_32)
                k1 *= C2_32
                hash = hash xor k1
            }

            1 -> {
                k1 = k1 xor (data[idx].toInt() and 0xff)

                k1 *= C1_32
                k1 = k1.rotateLeft(R1_32)
                k1 *= C2_32
                hash = hash xor k1
            }
        }

        // finalization
        hash = hash xor length
        return fmix32(hash)
    }

    /**
     * Murmur3 64-bit Hash值计算
     *
     * @param data 数据
     * @return Hash值
     */
    fun hash64(data: CharSequence): Long {
        return hash64(data.encodeToByteArray())
    }

    /**
     * Murmur3 64-bit 算法<br></br>
     * This is essentially MSB 8 bytes of Murmur3 128-bit variant.
     *
     * @param data 数据
     * @return Hash值
     */
    fun hash64(data: ByteArray): Long {
        return hash64(data, data.size, DEFAULT_SEED)
    }

    /**
     * 类Murmur3 64-bit 算法 <br></br>
     * This is essentially MSB 8 bytes of Murmur3 128-bit variant.
     *
     * @param data   数据
     * @param length 长度
     * @param seed   种子，默认0
     * @return Hash值
     */
    fun hash64(data: ByteArray, length: Int, seed: Int): Long {
        var hash = seed.toLong()
        val nblocks = length shr 3

        // body
        for (i in 0..<nblocks) {
            val i8 = i shl 3
            var k: Long = data.readLong(i8)

            // mix functions
            k *= C1
            k = k.rotateLeft(R1)
            k *= C2
            hash = hash xor k
            hash = hash.rotateLeft( R2) * M + N1
        }

        // tail
        var k1: Long = 0
        val tailStart = nblocks shl 3
        when (length - tailStart) {
            7 -> {
                k1 = k1 xor ((data[tailStart + 6].toLong() and 0xffL) shl 48)
                k1 = k1 xor ((data[tailStart + 5].toLong() and 0xffL) shl 40)
                k1 = k1 xor ((data[tailStart + 4].toLong() and 0xffL) shl 32)
                k1 = k1 xor ((data[tailStart + 3].toLong() and 0xffL) shl 24)
                k1 = k1 xor ((data[tailStart + 2].toLong() and 0xffL) shl 16)
                k1 = k1 xor ((data[tailStart + 1].toLong() and 0xffL) shl 8)
                k1 = k1 xor (data[tailStart].toLong() and 0xffL)
                k1 *= C1
                k1 = k1.rotateLeft(R1)
                k1 *= C2
                hash = hash xor k1
            }

            6 -> {
                k1 = k1 xor ((data[tailStart + 5].toLong() and 0xffL) shl 40)
                k1 = k1 xor ((data[tailStart + 4].toLong() and 0xffL) shl 32)
                k1 = k1 xor ((data[tailStart + 3].toLong() and 0xffL) shl 24)
                k1 = k1 xor ((data[tailStart + 2].toLong() and 0xffL) shl 16)
                k1 = k1 xor ((data[tailStart + 1].toLong() and 0xffL) shl 8)
                k1 = k1 xor (data[tailStart].toLong() and 0xffL)
                k1 *= C1
                k1 = k1.rotateLeft(R1)
                k1 *= C2
                hash = hash xor k1
            }

            5 -> {
                k1 = k1 xor ((data[tailStart + 4].toLong() and 0xffL) shl 32)
                k1 = k1 xor ((data[tailStart + 3].toLong() and 0xffL) shl 24)
                k1 = k1 xor ((data[tailStart + 2].toLong() and 0xffL) shl 16)
                k1 = k1 xor ((data[tailStart + 1].toLong() and 0xffL) shl 8)
                k1 = k1 xor (data[tailStart].toLong() and 0xffL)
                k1 *= C1
                k1 = k1.rotateLeft(R1)
                k1 *= C2
                hash = hash xor k1
            }

            4 -> {
                k1 = k1 xor ((data[tailStart + 3].toLong() and 0xffL) shl 24)
                k1 = k1 xor ((data[tailStart + 2].toLong() and 0xffL) shl 16)
                k1 = k1 xor ((data[tailStart + 1].toLong() and 0xffL) shl 8)
                k1 = k1 xor (data[tailStart].toLong() and 0xffL)
                k1 *= C1
                k1 = k1.rotateLeft(R1)
                k1 *= C2
                hash = hash xor k1
            }

            3 -> {
                k1 = k1 xor ((data[tailStart + 2].toLong() and 0xffL) shl 16)
                k1 = k1 xor ((data[tailStart + 1].toLong() and 0xffL) shl 8)
                k1 = k1 xor (data[tailStart].toLong() and 0xffL)
                k1 *= C1
                k1 = k1.rotateLeft(R1)
                k1 *= C2
                hash = hash xor k1
            }

            2 -> {
                k1 = k1 xor ((data[tailStart + 1].toLong() and 0xffL) shl 8)
                k1 = k1 xor (data[tailStart].toLong() and 0xffL)
                k1 *= C1
                k1 = k1.rotateLeft(R1)
                k1 *= C2
                hash = hash xor k1
            }

            1 -> {
                k1 = k1 xor (data[tailStart].toLong() and 0xffL)
                k1 *= C1
                k1 = k1.rotateLeft(R1)
                k1 *= C2
                hash = hash xor k1
            }
        }

        // finalization
        hash = hash xor length.toLong()
        hash = fmix64(hash)

        return hash
    }

    /**
     * Murmur3 128-bit Hash值计算
     *
     * @param data 数据
     * @return Hash值 (2 longs)
     */
    fun hash128(data: CharSequence): LongArray {
        return hash128(data.encodeToByteArray())
    }

    /**
     * Murmur3 128-bit 算法.
     *
     * @param data -数据
     * @return Hash值 (2 longs)
     */
    fun hash128(data: ByteArray): LongArray {
        return hash128(data, data.size, DEFAULT_SEED)
    }

    /**
     * Murmur3 128-bit variant.
     *
     * @param data   数据
     * @param length 长度
     * @param seed   种子，默认0
     * @return Hash值(2 longs)
     */
    fun hash128(data: ByteArray, length: Int, seed: Int): LongArray {
        return hash128(data, 0, length, seed)
    }

    /**
     * Murmur3 128-bit variant.
     *
     * @param data   数据
     * @param offset 数据开始位置
     * @param length 长度
     * @param seed   种子，默认0
     * @return Hash值(2 longs)
     */
    fun hash128(data: ByteArray, offset: Int, length: Int, seed: Int): LongArray {
        // 避免负数的种子
        var seed:Long = seed.toLong()
        seed = seed and 0xffffffffL

        var h1 = seed.toLong()
        var h2 = seed.toLong()
        val nblocks = length shr 4

        // body
        for (i in 0..<nblocks) {
            val i16 = offset + (i shl 4)
            var k1: Long = data.readLong(i16, DEFAULT_ORDER)
            var k2: Long = data.readLong( i16 + 8, DEFAULT_ORDER)

            // mix functions for k1
            k1 *= C1
            k1 = k1.rotateLeft(R1)
            k1 *= C2
            h1 = h1 xor k1
            h1 = h1.rotateLeft( R2)
            h1 += h2
            h1 = h1 * M + N1

            // mix functions for k2
            k2 *= C2
            k2 = k2.rotateLeft( R3)
            k2 *= C1
            h2 = h2 xor k2
            h2 = h2.rotateLeft( R1)
            h2 += h1
            h2 = h2 * M + N2
        }

        // tail
        var k1: Long = 0
        var k2: Long = 0
        val tailStart = offset + (nblocks shl 4)
        when (offset + length - tailStart) {
            15 -> {
                k2 = k2 xor ((data[tailStart + 14].toInt() and 0xff).toLong() shl 48)
                k2 = k2 xor ((data[tailStart + 13].toInt() and 0xff).toLong() shl 40)
                k2 = k2 xor ((data[tailStart + 12].toInt() and 0xff).toLong() shl 32)
                k2 = k2 xor ((data[tailStart + 11].toInt() and 0xff).toLong() shl 24)
                k2 = k2 xor ((data[tailStart + 10].toInt() and 0xff).toLong() shl 16)
                k2 = k2 xor ((data[tailStart + 9].toInt() and 0xff).toLong() shl 8)
                k2 = k2 xor (data[tailStart + 8].toInt() and 0xff).toLong()
                k2 *= C2
                k2 = k2.rotateLeft( R3)
                k2 *= C1
                h2 = h2 xor k2

                k1 = k1 xor ((data[tailStart + 7].toInt() and 0xff).toLong() shl 56)
                k1 = k1 xor ((data[tailStart + 6].toInt() and 0xff).toLong() shl 48)
                k1 = k1 xor ((data[tailStart + 5].toInt() and 0xff).toLong() shl 40)
                k1 = k1 xor ((data[tailStart + 4].toInt() and 0xff).toLong() shl 32)
                k1 = k1 xor ((data[tailStart + 3].toInt() and 0xff).toLong() shl 24)
                k1 = k1 xor ((data[tailStart + 2].toInt() and 0xff).toLong() shl 16)
                k1 = k1 xor ((data[tailStart + 1].toInt() and 0xff).toLong() shl 8)
                k1 = k1 xor (data[tailStart].toInt() and 0xff).toLong()
                k1 *= C1
                k1 = k1.rotateLeft( R1)
                k1 *= C2
                h1 = h1 xor k1
            }

            14 -> {
                k2 = k2 xor ((data[tailStart + 13].toInt() and 0xff).toLong() shl 40)
                k2 = k2 xor ((data[tailStart + 12].toInt() and 0xff).toLong() shl 32)
                k2 = k2 xor ((data[tailStart + 11].toInt() and 0xff).toLong() shl 24)
                k2 = k2 xor ((data[tailStart + 10].toInt() and 0xff).toLong() shl 16)
                k2 = k2 xor ((data[tailStart + 9].toInt() and 0xff).toLong() shl 8)
                k2 = k2 xor (data[tailStart + 8].toInt() and 0xff).toLong()
                k2 *= C2
                k2 = k2.rotateLeft( R3)
                k2 *= C1
                h2 = h2 xor k2

                k1 = k1 xor ((data[tailStart + 7].toInt() and 0xff).toLong() shl 56)
                k1 = k1 xor ((data[tailStart + 6].toInt() and 0xff).toLong() shl 48)
                k1 = k1 xor ((data[tailStart + 5].toInt() and 0xff).toLong() shl 40)
                k1 = k1 xor ((data[tailStart + 4].toInt() and 0xff).toLong() shl 32)
                k1 = k1 xor ((data[tailStart + 3].toInt() and 0xff).toLong() shl 24)
                k1 = k1 xor ((data[tailStart + 2].toInt() and 0xff).toLong() shl 16)
                k1 = k1 xor ((data[tailStart + 1].toInt() and 0xff).toLong() shl 8)
                k1 = k1 xor (data[tailStart].toInt() and 0xff).toLong()
                k1 *= C1
                k1 = k1.rotateLeft( R1)
                k1 *= C2
                h1 = h1 xor k1
            }

            13 -> {
                k2 = k2 xor ((data[tailStart + 12].toInt() and 0xff).toLong() shl 32)
                k2 = k2 xor ((data[tailStart + 11].toInt() and 0xff).toLong() shl 24)
                k2 = k2 xor ((data[tailStart + 10].toInt() and 0xff).toLong() shl 16)
                k2 = k2 xor ((data[tailStart + 9].toInt() and 0xff).toLong() shl 8)
                k2 = k2 xor (data[tailStart + 8].toInt() and 0xff).toLong()
                k2 *= C2
                k2 = k2.rotateLeft( R3)
                k2 *= C1
                h2 = h2 xor k2

                k1 = k1 xor ((data[tailStart + 7].toInt() and 0xff).toLong() shl 56)
                k1 = k1 xor ((data[tailStart + 6].toInt() and 0xff).toLong() shl 48)
                k1 = k1 xor ((data[tailStart + 5].toInt() and 0xff).toLong() shl 40)
                k1 = k1 xor ((data[tailStart + 4].toInt() and 0xff).toLong() shl 32)
                k1 = k1 xor ((data[tailStart + 3].toInt() and 0xff).toLong() shl 24)
                k1 = k1 xor ((data[tailStart + 2].toInt() and 0xff).toLong() shl 16)
                k1 = k1 xor ((data[tailStart + 1].toInt() and 0xff).toLong() shl 8)
                k1 = k1 xor (data[tailStart].toInt() and 0xff).toLong()
                k1 *= C1
                k1 = k1.rotateLeft( R1)
                k1 *= C2
                h1 = h1 xor k1
            }

            12 -> {
                k2 = k2 xor ((data[tailStart + 11].toInt() and 0xff).toLong() shl 24)
                k2 = k2 xor ((data[tailStart + 10].toInt() and 0xff).toLong() shl 16)
                k2 = k2 xor ((data[tailStart + 9].toInt() and 0xff).toLong() shl 8)
                k2 = k2 xor (data[tailStart + 8].toInt() and 0xff).toLong()
                k2 *= C2
                k2 = k2.rotateLeft( R3)
                k2 *= C1
                h2 = h2 xor k2

                k1 = k1 xor ((data[tailStart + 7].toInt() and 0xff).toLong() shl 56)
                k1 = k1 xor ((data[tailStart + 6].toInt() and 0xff).toLong() shl 48)
                k1 = k1 xor ((data[tailStart + 5].toInt() and 0xff).toLong() shl 40)
                k1 = k1 xor ((data[tailStart + 4].toInt() and 0xff).toLong() shl 32)
                k1 = k1 xor ((data[tailStart + 3].toInt() and 0xff).toLong() shl 24)
                k1 = k1 xor ((data[tailStart + 2].toInt() and 0xff).toLong() shl 16)
                k1 = k1 xor ((data[tailStart + 1].toInt() and 0xff).toLong() shl 8)
                k1 = k1 xor (data[tailStart].toInt() and 0xff).toLong()
                k1 *= C1
                k1 = k1.rotateLeft( R1)
                k1 *= C2
                h1 = h1 xor k1
            }

            11 -> {
                k2 = k2 xor ((data[tailStart + 10].toInt() and 0xff).toLong() shl 16)
                k2 = k2 xor ((data[tailStart + 9].toInt() and 0xff).toLong() shl 8)
                k2 = k2 xor (data[tailStart + 8].toInt() and 0xff).toLong()
                k2 *= C2
                k2 = k2.rotateLeft(R3)
                k2 *= C1
                h2 = h2 xor k2

                k1 = k1 xor ((data[tailStart + 7].toInt() and 0xff).toLong() shl 56)
                k1 = k1 xor ((data[tailStart + 6].toInt() and 0xff).toLong() shl 48)
                k1 = k1 xor ((data[tailStart + 5].toInt() and 0xff).toLong() shl 40)
                k1 = k1 xor ((data[tailStart + 4].toInt() and 0xff).toLong() shl 32)
                k1 = k1 xor ((data[tailStart + 3].toInt() and 0xff).toLong() shl 24)
                k1 = k1 xor ((data[tailStart + 2].toInt() and 0xff).toLong() shl 16)
                k1 = k1 xor ((data[tailStart + 1].toInt() and 0xff).toLong() shl 8)
                k1 = k1 xor (data[tailStart].toInt() and 0xff).toLong()
                k1 *= C1
                k1 = k1.rotateLeft(R1)
                k1 *= C2
                h1 = h1 xor k1
            }

            10 -> {
                k2 = k2 xor ((data[tailStart + 9].toInt() and 0xff).toLong() shl 8)
                k2 = k2 xor (data[tailStart + 8].toInt() and 0xff).toLong()
                k2 *= C2
                k2 = k2.rotateLeft(R3)
                k2 *= C1
                h2 = h2 xor k2

                k1 = k1 xor ((data[tailStart + 7].toInt() and 0xff).toLong() shl 56)
                k1 = k1 xor ((data[tailStart + 6].toInt() and 0xff).toLong() shl 48)
                k1 = k1 xor ((data[tailStart + 5].toInt() and 0xff).toLong() shl 40)
                k1 = k1 xor ((data[tailStart + 4].toInt() and 0xff).toLong() shl 32)
                k1 = k1 xor ((data[tailStart + 3].toInt() and 0xff).toLong() shl 24)
                k1 = k1 xor ((data[tailStart + 2].toInt() and 0xff).toLong() shl 16)
                k1 = k1 xor ((data[tailStart + 1].toInt() and 0xff).toLong() shl 8)
                k1 = k1 xor (data[tailStart].toInt() and 0xff).toLong()
                k1 *= C1
                k1 = k1.rotateLeft(R1)
                k1 *= C2
                h1 = h1 xor k1
            }

            9 -> {
                k2 = k2 xor (data[tailStart + 8].toInt() and 0xff).toLong()
                k2 *= C2
                k2 = k2.rotateLeft(R3)
                k2 *= C1
                h2 = h2 xor k2

                k1 = k1 xor ((data[tailStart + 7].toInt() and 0xff).toLong() shl 56)
                k1 = k1 xor ((data[tailStart + 6].toInt() and 0xff).toLong() shl 48)
                k1 = k1 xor ((data[tailStart + 5].toInt() and 0xff).toLong() shl 40)
                k1 = k1 xor ((data[tailStart + 4].toInt() and 0xff).toLong() shl 32)
                k1 = k1 xor ((data[tailStart + 3].toInt() and 0xff).toLong() shl 24)
                k1 = k1 xor ((data[tailStart + 2].toInt() and 0xff).toLong() shl 16)
                k1 = k1 xor ((data[tailStart + 1].toInt() and 0xff).toLong() shl 8)
                k1 = k1 xor (data[tailStart].toInt() and 0xff).toLong()
                k1 *= C1
                k1 = k1.rotateLeft(R1)
                k1 *= C2
                h1 = h1 xor k1
            }

            8 -> {
                k1 = k1 xor ((data[tailStart + 7].toInt() and 0xff).toLong() shl 56)
                k1 = k1 xor ((data[tailStart + 6].toInt() and 0xff).toLong() shl 48)
                k1 = k1 xor ((data[tailStart + 5].toInt() and 0xff).toLong() shl 40)
                k1 = k1 xor ((data[tailStart + 4].toInt() and 0xff).toLong() shl 32)
                k1 = k1 xor ((data[tailStart + 3].toInt() and 0xff).toLong() shl 24)
                k1 = k1 xor ((data[tailStart + 2].toInt() and 0xff).toLong() shl 16)
                k1 = k1 xor ((data[tailStart + 1].toInt() and 0xff).toLong() shl 8)
                k1 = k1 xor (data[tailStart].toInt() and 0xff).toLong()
                k1 *= C1
                k1 = k1.rotateLeft(R1)
                k1 *= C2
                h1 = h1 xor k1
            }

            7 -> {
                k1 = k1 xor ((data[tailStart + 6].toInt() and 0xff).toLong() shl 48)
                k1 = k1 xor ((data[tailStart + 5].toInt() and 0xff).toLong() shl 40)
                k1 = k1 xor ((data[tailStart + 4].toInt() and 0xff).toLong() shl 32)
                k1 = k1 xor ((data[tailStart + 3].toInt() and 0xff).toLong() shl 24)
                k1 = k1 xor ((data[tailStart + 2].toInt() and 0xff).toLong() shl 16)
                k1 = k1 xor ((data[tailStart + 1].toInt() and 0xff).toLong() shl 8)
                k1 = k1 xor (data[tailStart].toInt() and 0xff).toLong()
                k1 *= C1
                k1 = k1.rotateLeft(R1)
                k1 *= C2
                h1 = h1 xor k1
            }

            6 -> {
                k1 = k1 xor ((data[tailStart + 5].toInt() and 0xff).toLong() shl 40)
                k1 = k1 xor ((data[tailStart + 4].toInt() and 0xff).toLong() shl 32)
                k1 = k1 xor ((data[tailStart + 3].toInt() and 0xff).toLong() shl 24)
                k1 = k1 xor ((data[tailStart + 2].toInt() and 0xff).toLong() shl 16)
                k1 = k1 xor ((data[tailStart + 1].toInt() and 0xff).toLong() shl 8)
                k1 = k1 xor (data[tailStart].toInt() and 0xff).toLong()
                k1 *= C1
                k1 = k1.rotateLeft(R1)
                k1 *= C2
                h1 = h1 xor k1
            }

            5 -> {
                k1 = k1 xor ((data[tailStart + 4].toInt() and 0xff).toLong() shl 32)
                k1 = k1 xor ((data[tailStart + 3].toInt() and 0xff).toLong() shl 24)
                k1 = k1 xor ((data[tailStart + 2].toInt() and 0xff).toLong() shl 16)
                k1 = k1 xor ((data[tailStart + 1].toInt() and 0xff).toLong() shl 8)
                k1 = k1 xor (data[tailStart].toInt() and 0xff).toLong()
                k1 *= C1
                k1 = k1.rotateLeft(R1)
                k1 *= C2
                h1 = h1 xor k1
            }

            4 -> {
                k1 = k1 xor ((data[tailStart + 3].toInt() and 0xff).toLong() shl 24)
                k1 = k1 xor ((data[tailStart + 2].toInt() and 0xff).toLong() shl 16)
                k1 = k1 xor ((data[tailStart + 1].toInt() and 0xff).toLong() shl 8)
                k1 = k1 xor (data[tailStart].toInt() and 0xff).toLong()
                k1 *= C1
                k1 = k1.rotateLeft(R1)
                k1 *= C2
                h1 = h1 xor k1
            }

            3 -> {
                k1 = k1 xor ((data[tailStart + 2].toInt() and 0xff).toLong() shl 16)
                k1 = k1 xor ((data[tailStart + 1].toInt() and 0xff).toLong() shl 8)
                k1 = k1 xor (data[tailStart].toInt() and 0xff).toLong()
                k1 *= C1
                k1 = k1.rotateLeft(R1)
                k1 *= C2
                h1 = h1 xor k1
            }

            2 -> {
                k1 = k1 xor ((data[tailStart + 1].toInt() and 0xff).toLong() shl 8)
                k1 = k1 xor (data[tailStart].toInt() and 0xff).toLong()
                k1 *= C1
                k1 = k1.rotateLeft(R1)
                k1 *= C2
                h1 = h1 xor k1
            }

            1 -> {
                k1 = k1 xor (data[tailStart].toInt() and 0xff).toLong()
                k1 *= C1
                k1 = k1.rotateLeft(R1)
                k1 *= C2
                h1 = h1 xor k1
            }
        }

        // finalization
        h1 = h1 xor length.toLong()
        h2 = h2 xor length.toLong()

        h1 += h2
        h2 += h1

        h1 = fmix64(h1)
        h2 = fmix64(h2)

        h1 += h2
        h2 += h1

        return longArrayOf(h1, h2)
    }

    private fun mix32(k: Int, hash: Int): Int {
        var k = k
        var hash = hash
        k *= C1_32
        k = k.rotateLeft( R1_32)
        k *= C2_32
        hash = hash xor k
        return hash.rotateLeft( R2_32) * M_32 + N_32
    }

    private fun fmix32(hash: Int): Int {
        var hash = hash
        hash = hash xor (hash ushr 16)
        hash *= -0x7a143595
        hash = hash xor (hash ushr 13)
        hash *= -0x3d4d51cb
        hash = hash xor (hash ushr 16)
        return hash
    }

    private fun fmix64(h: Long): Long {
        var h = h
        h = h xor (h ushr 33)
        h *= -0xae502812aa7333L
        h = h xor (h ushr 33)
        h *= -0x3b314601e57a13adL
        h = h xor (h ushr 33)
        return h
    }
}
